// import { createApp } from 'vue';
import App from './App.vue';
import {createRenderer, createApp, nextTick} from '@vue/runtime-dom'; //拿到创建渲染器的方法

// 原生vue3中可以直接创建应用
// createApp(App).mount('#app');

// 但如果我希望底层变成小程序
// 小程序是没有dom结构 而只有setData接口
// 以前需要把vue源码拷贝下来自己改
// 现在提供了自定义渲染器解决这个问题
const d2a = (n) => {
  return n * Math.PI / 180;
};

const drawCircle = (start, end, color, cx, cy, r) => {
  let x = cx + Math.cos(d2a(start)) * r;
  let y = cy + Math.sin(d2a(start)) * r;

  ctx.beginPath();
  ctx.moveTo(cx, cy);
  ctx.lineTo(x, y);
  ctx.arc(cx,cy,r,d2a(start),d2a(end));
  ctx.fillStyle = color;
  ctx.fill();
  ctx.stroke(); //线条方式展示

  ctx.closePath();
};
const draw = (el, noClear) => {
  if(!noClear){
    ctx.clearRect(0, 0, canvas.width, canvas.height); //每次渲染前 先清掉之前的
  }

  // 要识别哪些我能渲染
  if(el.tag === 'circle'){
    console.log('遇到了一个 circle 应该用canvas 来绘制');
    const {data, x, y, r} = el;
    //console.log(data, x, y, r); //用canvas来画饼图

    //饼图 总共数量 每个占比
    let total = data.reduce((memo, current) => memo + current.count, 0);

    //一共360° 每个占多少°
    let start = 0, end = 0;

    data.forEach(item => {
      end += item.count / total * 360;

      drawCircle(start, end, item.color, x, y, r);

      start = end;
    });
  }

  if(el.tag === 'rect'){
    console.log('开始绘制矩形');
  }

  el.childs && el.childs.forEach(child => {
    draw(child,true);
  });
};

/** ↓自己重新定义的方法, vue会调用*/
const ops = {
  /** dom操作api*/
  insert: (child, parent, anchor) => {
    console.log('insert~~~~~~~~~~~~~~~~~~~');
    // 插入的过程是由子及父 先将circle插入到div中 再将div插入到canvas
    console.log('child:',child);
    console.log('parent:',parent);
    console.log('-------------------------');

    // 插入时 先创建父子关系,等会渲染的时候会用到
    child.parent = parent;
    // dom元素的children只能读不能写(故下面用了.childs)
    if(!parent.childs){
      parent.childs = [child]
    }else {
      parent.childs.push(child);
    }

    //由于 我们在createElement中返回的都是{tag}
    //故只有根节点 会走这里<div id="app"</div>
    if(parent.nodeType === 1){ //遇到canvas了, 我就需要重新将整颗树渲染到canvas中
      //console.log(parent)
      draw(child);

      if(child.onClick){
        // 需要注意的是 我们App.vue里写的dom 和 自定义标签 并没有真正插入到#app中
        // 我们在createElement方法中将其变为了虚拟节点 以供 canvas得到这些节点信息来绘制图形(canvas才是真正插入到#app中的)
        canvas.addEventListener('click',()=>{
          child.onClick(); //触发定义的事件
          nextTick(() => {
            draw(child); //重新绘制
          });
        })
      }
    }
  },

  remove: child => {

  },

  createElement: (tag, isSVG, is) => {
    console.log('createElement~~~~~~~~~~~~~~~~~~~');
    // 创建元素的过程 深度遍历的过程 有儿子 先创建儿子 将儿子绑定到父亲上

    console.log('tag:',tag); //div(string) //circle(string)
    console.log('-------------------------');
    return {tag}; // 这里返回的 会作为insert接受的child和parent
  },

  createText: text => {},

  createComment: text => {},

  setText: (node, text) => {},

  setElementText: (el, text) => {},

  parentNode: node => {},

  nextSibling: node => {},

  querySelector: selector => {},

  setScopeId(el, id) {},

  cloneNode(el) {},

  // __UNSAFE__
  // Reason: innerHTML.
  // Static content here can only come from compiled templates.
  // As long as the user only uses trusted templates, this is safe.
  insertStaticContent(content, parent, anchor, isSVG) {

  },

  /** 有元素的属性发生改变才会触发(没有变化就不会触发,即使重新被赋了一个相同的值)*/
  patchProp(el, key, prevValue, nextValue) {
    console.log('patchProp~~~~~~~~~~~~~~~~~~~');
    console.log('el:',el);
    console.log('key:',key);
    console.log('prevValue:',prevValue);
    console.log('nextValue:',nextValue);
    el[key] = nextValue;
    console.log('-------------------------');
  }
};
let canvas;
let ctx;
const createCanvasApp = (...args) => {
  const app = createRenderer(ops).createApp(...args); // app.mount

  const {mount} = app; //内部的mount方法

  app.mount = function (selector) {
    let el = document.querySelector(selector);

    //这里写你自己的逻辑
    canvas = document.createElement('canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    el.appendChild(canvas);
    ctx = canvas.getContext('2d');

    mount(canvas);
  };

  return app;
};

createCanvasApp(App).mount('#app');
